//
//  GameModel.m
//  CoreGraphicsTest
//
//  Created by aewatson on 11/19/11.
//  Copyright 2011 __MyCompanyName__. All rights reserved.
//

#import "GameModel.h"

@implementation GameModel

@synthesize blocks;
@synthesize shots;
@synthesize explosions;
@synthesize round;
@synthesize score;
@synthesize viewWidth;
@synthesize viewHeight;
@synthesize state;
@synthesize blockInterval;
@synthesize curBlockInterval;
@synthesize curBlockStateInterval;
@synthesize curBlock;
@synthesize blockCount;
@synthesize lastBlock;
@synthesize blocksHitCount;
@synthesize velocityX;

-(id) init {
    self = [super init];
    
    // initialize random with seed
    srandom(time(NULL));
    
    // init static data members
    viewWidth = VIEW_WIDTH;
    viewHeight = VIEW_HEIGHT;
    
    // init 1st round
    round = 0;
    [self initNextRound];
    
    // finish
    return self;
}

-(void)initNextRound {
    
    // initialize vars
    state = NOT_STARTED;
    round++;
    
    // initialize blocks, shots, explosions
    blocks = [[NSMutableArray alloc] initWithCapacity:10];
    shots = [[NSMutableArray alloc] initWithCapacity:30];
    explosions = [[NSMutableArray alloc] initWithCapacity:10];

}

-(void)startRound {
    
    // set number of milliseconds between block introductions based on round
    blockInterval = BLOCK_INTERVAL_START + (round - 1) * BLOCK_INTERVAL_INC;
    
    // set number of blocks in round
    blockCount = BLOCK_COUNT_START + (round - 1) * BLOCK_COUNT_INC;
    
    // get next block interval
    curBlockInterval = [self getNextBlockInterval];
    
    // set default velocity X for blocks
    velocityX = BLOCK_VELOCITY_X_START + (round - 1) * BLOCK_VELOCITY_X_INC;
    
    // reset block count
    curBlock = 0;
    blocksHitCount = 0;
    lastBlock = false;
    
    // change game state
    state = PLAYING;
    NSLog(@"state change from NOT_STARTED to PLAYING");
    
}

-(void)endRound {
    [self initNextRound];
    [[NSNotificationCenter defaultCenter] postNotificationName:@"roundEnded" object:self];
}

-(NSString*)getStatus {
    return [[[NSString alloc] initWithFormat:@"Round %d!", round] autorelease];
}

-(NSString*)getSummary {
    return [[[NSString alloc] initWithFormat:@"Hit %d of %d blocks", blocksHitCount, blockCount] autorelease];
}

-(NSString*)getScoreString {
    return [[[NSString alloc] initWithFormat:@"Points: %d", score] autorelease];
}

-(NSString*)getRoundString {
    return [[[NSString alloc] initWithFormat:@"Round: %d", round] autorelease];
}

-(int) getNextBlockInterval {
    // get random percentage
    int percent = random() % 100;
    // get random negative or positive
    int negpos = random() % 2;
    int factor;
    if(negpos == 0) factor = 1;
    else factor = -1;
    // get interval variance
    float variance = BLOCK_INTERVAL_VAR_PERCENT * percent / 100 * blockInterval;
    variance *= factor;
    // return next block interval with random variance
    return blockInterval + variance;
}

+(float) getAcceleration {
    return ACCELERATION;
}

-(DIRECTION) getRandomDirection {
    int d = random() % 2;
    if(d==0) return LEFT;
    return RIGHT;
}

-(COLOR) getRandomColor {
    return random() % 4;
}

-(int) getVelocityY {
    // get random additional velocity
    int yAdd = random() % BLOCK_VELOCITY_VAR;
    // get random +/-
    int negpos = random() % 2;
    int factor;
    if(negpos == 0) factor = 1;
    else factor = -1;
    //
    return BLOCK_VELOCITY_Y + factor * yAdd;
}

-(int) getVelocityX {
    // get random additional velocity
    int xAdd = random() % BLOCK_VELOCITY_VAR;
    // get random +/-
    int negpos = random() % 2;
    int factor;
    if(negpos == 0) factor = 1;
    else factor = -1;
    //
    return velocityX + factor * xAdd;
}

-(void) addBlock {
    // create block
    SpriteModel *block = [[[SpriteModel alloc] initAsBlockWithDirection:[self getRandomDirection] color:[self getRandomColor] velocityX:[self getVelocityX] andVelocityY:[self getVelocityY]] autorelease];
    // add to collection
    [[self blocks] addObject:block];
    // notify listeners that a block was created
    [[NSNotificationCenter defaultCenter] postNotificationName:@"blockAdded" object:[[self blocks] objectAtIndex:[[self blocks] count] - 1]];
}

-(void) addShotWithColor:(int) shotColor {
    // create shot
    SpriteModel *shot = [[[SpriteModel alloc] initAsShotWithColor:shotColor] autorelease];
    // add to collection
    [[self shots] addObject:shot];
    // notify listeners that a shot was created
    [[NSNotificationCenter defaultCenter] postNotificationName:@"shotAdded" object:[[self shots] objectAtIndex:[[self shots] count] - 1]];
}

-(void) addExplosionWithBlock:(SpriteModel*) block {
    // create explosion
    SpriteModel *explosion = [[[SpriteModel alloc] initAsExplosionWithBlock:block] autorelease];
    // add to collection
    [[self explosions] addObject:explosion];
    // notify listeners that a explosion was created
    [[NSNotificationCenter defaultCenter] postNotificationName:@"explosionAdded" object:[[self explosions] objectAtIndex:[[self explosions] count] - 1]];
}

-(void) updateModelAfterMilliseconds:(int) ms {
    
    // make list for blocks to remove
    NSMutableArray *blocksToRemove = [[[NSMutableArray alloc] initWithCapacity:[blocks count]] autorelease];
    NSMutableArray *explosionsToRemove = [[[NSMutableArray alloc] initWithCapacity:[explosions count]] autorelease];
    NSMutableArray *shotsToRemove = [[[NSMutableArray alloc] initWithCapacity:[shots count]] autorelease];
    
    /*
     * This section deals with shots, moving them, and queuing them for removal if off screen
     */
    
    // iterate through shots
    for(id shot in shots) {
        // move shot
        [shot locationAfterMilliseconds:ms];
        // if off screen
        if(![self isModelVisible:shot]) {
            // add to list of explosions to be removed
            [shotsToRemove addObject:shot];
        }
    }
    
    /*
     * This section moves explosions, changes there images, and queues for removal if off screen
     */
    
    // iterate through explosions
    for(id explosion in explosions) {
        // move explosion
        [explosion locationAfterMilliseconds:ms];
        // if off screen
        if(![self isModelVisible:explosion]) {
            // add to list of explosions to be removed
            [explosionsToRemove addObject:explosion];
        }
        // otherwise advance image if it's time
        else if([explosion nextState]) {
            [[NSNotificationCenter defaultCenter] postNotificationName:@"spriteStateChanged" object:explosion];
        }
    }
    
    /*
     * This section moves blocks, determines if they were hit, and queues them for removal if off screen
     */
    
    // iterate through blocks
    for(id block in blocks) {
        // move block
        [block locationAfterMilliseconds:ms];
        
        // iterate through all shots
        for(id shot in shots) {
            // if shot color is same as block...
            if([shot getColor] == [block getColor]) {
                // and if boundaries intersect...
                if(CGRectIntersectsRect([shot getRect], [block getRect])) {
                    // remove block
                    [blocksToRemove addObject:block];
                    // remove shot
                    [shotsToRemove addObject:shot];
                    // replace with explosion animation
                    [self addExplosionWithBlock:block];
                    // score
                    score += BLOCK_POINTS;
                    blocksHitCount++;
                    [[NSNotificationCenter defaultCenter] postNotificationName:@"scoreChanged" object:self];
                }
            }
        }
        
        // if block moved off screen
        if(![self isModelVisible:block]) {
            // add to list of blocks to be removed
            [blocksToRemove addObject:block];
        }
    }
    
    /*
     * This section removes objects from screen and memory
     */
    
    // remove all blocks that moved off screen
    for (id block in blocksToRemove) {
        [[self blocks] removeObject:block];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"spriteRemoved" object:block];
        [block release];
    }
    
    // remove all explosions that moved off screen
    for (id explosion in explosionsToRemove) {
        [[self explosions] removeObject:explosion];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"spriteRemoved" object:explosion];
        [explosion release];
    }
    
    // remove all shot that moved off screen
    for (id shot in shotsToRemove) {
        [[self shots] removeObject:shot];
        [[NSNotificationCenter defaultCenter] postNotificationName:@"spriteRemoved" object:shot];
        [shot release];
    }
    
    /* 
     * This section determines if the round is over and adds blocks if not 
     */
    
    // if last block was thrown...
    if(lastBlock) {
        // if nothing left on screen...
        if([blocks count] == 0 && [explosions count] == 0 && [shots count] == 0) {
            // end the round
            [self setState:FINISHED];
        }
    }
    else {
        // if it's time to add a block...
        if(curBlockInterval < 0) {
            // add block
            [self addBlock];
            curBlock++;
            // if it was the last block
            if(curBlock == blockCount) {
                // set flag
                lastBlock = true;
            }
            // otherwise, get the next block interval
            else {
                curBlockInterval = [self getNextBlockInterval];
            }
        }
        else {
            curBlockInterval -= ms;
        }

    }
    
}

-(bool) isModelVisible:(SpriteModel*) spriteModel {
    return spriteModel.centerX > 0 && spriteModel.centerX < VIEW_WIDTH && spriteModel.centerY > 0 && spriteModel.centerY < VIEW_HEIGHT;
}

-(void) timerTickedAfterMilliseconds:(int) ms {
    switch (state) {
        case NOT_STARTED:
            // first tick, set game to playing
            [self startRound];            
            break;
        case PLAYING:
            //NSLog(@"state is PLAYING");
            [self updateModelAfterMilliseconds:ms];
            break;
        case FINISHED:
            //NSLog(@"state is FINISHED");
            [self endRound];
            break;
        default:
            break;
    }
    
}

@end
